package yhao.demo.autocode.core.feignmapper;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor;
import yhao.infra.common.model.ModelDocDef;
import yhao.infra.common.model.ModelPropertyDef;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

public class FeignDefinition {
    //类名
    private String className;
    //方法序列
    private List<FeignMethodDefinition> methodDefinitions = new ArrayList<>();

    private String getShortClassName(String className){
        if(className.indexOf(".")>-1){
            return className.substring(className.lastIndexOf(".")+1);
        }else{
            return className;
        }
    }

    public FeignDefinition(AnnotationMetadataReadingVisitor classMeta) {
        this.className = getShortClassName(classMeta.getClassName());

        try {
            Class type = Class.forName(classMeta.getClassName());
            Method[] methods = type.getDeclaredMethods();
            for(Method method:methods){
                ModelDocDef def = method.getDeclaredAnnotation(ModelDocDef.class);
                if(def==null){
                    continue;
                }
                FeignMethodDefinition methodDef = new FeignMethodDefinition();
                methodDef.methodName = method.getName();
                methodDef.desc = def.value();
                analysisReturnType(methodDef,method.getGenericReturnType().toString());
                analysisParameterType(methodDef,method.getParameters());
                methodDefinitions.add(methodDef);
            }
        } catch (Exception e) {}
    }

    private void analysisParameterType(FeignMethodDefinition methodDef, Parameter[] parameters) {
        if(parameters!=null){
            for(Parameter p:parameters){
                methodDef.parameterType = getShortClassName(p.getType().toString());
                Class type = p.getType();
                ApiModel apiModel = (ApiModel) type.getDeclaredAnnotation(ApiModel.class);
                if(apiModel!=null){
                    methodDef.parameterTypeDesc = apiModel.value();
                }
                Field[] fields = type.getDeclaredFields();
                if(fields!=null){
                    for(Field f:fields){
                        ApiModelProperty property = f.getDeclaredAnnotation(ApiModelProperty.class);
                        ModelPropertyDef pd = f.getDeclaredAnnotation(ModelPropertyDef.class);
                        StringBuffer sbKey= new StringBuffer();
                        if(property!=null && !property.hidden()){
                            StringBuffer sbInfo = new StringBuffer(property.value());
                            if(property.required()){
                                sbKey.append("[必填]");
                            }else{
                                sbKey.append("[选填]");
                            }
                            if(StringUtils.isNotBlank(property.example())){
                                sbInfo.append("{").append(property.example()).append("}");
                            }
                            methodDef.parameterMap.put(sbKey.append(f.getName()).toString(),sbInfo.toString());
                        }else if(pd!=null){
                            if(pd.required()){
                                sbKey.append("[必填]");
                            }else{
                                sbKey.append("[选填]");
                            }
                            methodDef.parameterMap.put(sbKey.append(f.getName()).toString(),pd.value());
                        }
                    }
                }
            }
        }
    }

    private void analysisReturnType(FeignMethodDefinition methodDef, String returnType) {
        int startIndex = returnType.indexOf("<");
        String genericType = null;
        while(startIndex>-1){
            int endIndex = returnType.lastIndexOf(">");
            genericType = returnType.substring(0,startIndex);
            returnType = returnType.substring(startIndex+1,endIndex);
            startIndex = returnType.indexOf("<");
        }
        if(genericType!=null){
            methodDef.genericType = getShortClassName(genericType);
        }
        methodDef.returnType = getShortClassName(returnType);
        try {
            Class type = Class.forName(returnType);
            ApiModel apiModel = (ApiModel) type.getDeclaredAnnotation(ApiModel.class);
            if(apiModel!=null){
                methodDef.returnTypeDesc = StringUtils.isEmpty(apiModel.value())
                        ?apiModel.description():apiModel.value();
            }
            Field[] fields = type.getDeclaredFields();
            if(fields!=null){
                for(Field f:fields){
                    ApiModelProperty property = f.getDeclaredAnnotation(ApiModelProperty.class);
                    ModelPropertyDef pd = f.getDeclaredAnnotation(ModelPropertyDef.class);
                    if(property!=null && !property.hidden()){
                        methodDef.returnMap.put(f.getName(),property.value());
                    }else if(pd!=null){
                        methodDef.returnMap.put(f.getName(),pd.value());
                    }
                }
            }
        } catch (ClassNotFoundException e) {}
    }

    public String getClassName() {
        return className;
    }

    public List<FeignMethodDefinition> getMethodDefinitions() {
        return methodDefinitions;
    }

    static public class FeignMethodDefinition{
        //方法名
        private String methodName;
        //方法描述
        private String desc;
        //泛型类型
        private String genericType;
        //返回值类型
        private String returnType;
        //返回值类型描述
        private String returnTypeDesc;
        //传参类型
        private String parameterType;
        //传参类型描述
        private String parameterTypeDesc;
        //返回类型字段说明
        private LinkedHashMap<String,String> returnMap = new LinkedHashMap();
        //传参字段说明
        private LinkedHashMap<String,String> parameterMap = new LinkedHashMap();

        public String getMethodName() {
            return methodName;
        }

        public String getDesc() {
            return desc;
        }

        public String getGenericType() {
            return genericType;
        }

        public String getReturnType() {
            return returnType;
        }

        public String getReturnTypeDesc() {
            return returnTypeDesc;
        }

        public String getParameterType() {
            return parameterType;
        }

        public String getParameterTypeDesc() {
            return parameterTypeDesc;
        }

        public LinkedHashMap<String, String> getReturnMap() {
            return returnMap;
        }

        public LinkedHashMap<String, String> getParameterMap() {
            return parameterMap;
        }
    }
}
